package model

import (
	"fmt"
	"gitee.com/tang_q/gin-vue3-admin-api/global"
	"gitee.com/tang_q/gin-vue3-admin-api/model/dto"
	"gitee.com/tang_q/gin-vue3-admin-api/utils"
	"gorm.io/gorm"
	"strconv"
)

type Roles struct {
	gorm.Model
	Name       string  `json:"name" gorm:"comment:角色名称;unique"`
	Describe   string  `json:"describe" gorm:"type:varchar(255);comment:角色描述;"`
	Creator    *Users  `json:"creator" gorm:"comment:创建者;foreignKey:CreatorID" `
	CreatorID  uint    `json:"-"`
	Modifier   *Users  `json:"modifier" gorm:"comment:更新者;foreignKey:ModifierID"`
	ModifierID uint    `json:"-"`
	Menus      []Menus `gorm:"many2many:roles_menus;"`
	Users      []Users `gorm:"many2many:users_roles;" json:"-"`
	IsUsable   uint8   `json:"isUsable" gorm:"comment:是否启用1:启用,0:禁用"`
}

func init() {
	fmt.Println(global.DB)
	err := global.DB.AutoMigrate(&Roles{})
	if err != nil {
		global.Logger.Error("Roles表自动迁移数据失败!", err)
		return
	}
}
func (r *Roles) BeforeDelete(*gorm.DB) error {
	if r.Name == "超级管理员" {
		return fmt.Errorf("不能对超级管理员进行操作")
	}
	return nil
}
func GetRolesByIds(ids []uint) (roles []Roles, err error) {
	err = global.DB.Find(&roles, ids).Error
	return
}

// 获取所有角色
func GetRoles() (roles []Roles, err error) {
	err = global.DB.Find(&roles).Error
	return
}

func GetRoleList(reqData dto.RoleFilterAndPaginationReq) (roles []Roles, count int64, err error) {
	qs := global.DB.Model(&Roles{})
	if reqData.Name != "" {
		qs = global.DB.Where("name LIKE ?", "%"+reqData.Name+"%")
	}
	if reqData.IsUsable == 0 || reqData.IsUsable == 1 {
		qs = qs.Where("is_usable = ?", reqData.IsUsable)
	}
	qs.Count(&count)
	if err := qs.Preload("Creator").Preload("Modifier").Offset((reqData.CurrentPage - 1) * reqData.PageSize).Limit(reqData.PageSize).Find(&roles).Error; err != nil {
		return nil, 0, err
	}
	return
}

func GetRoleByID(id string) (role Roles, err error) {
	err = global.DB.Preload("Menus").First(&role, id).Error
	if err != nil {
		return
	}
	return
}

// 创建角色
func CreateRole(reqData dto.CreateRoleDTO, createUser *Users) error {
	menus, err := GetMenusByIds(reqData.MenuIDs)
	if err != nil {
		return err
	}
	role := Roles{
		Name:       reqData.Name,
		Describe:   reqData.Describe,
		IsUsable:   reqData.IsUsable,
		CreatorID:  createUser.ID,
		ModifierID: createUser.ID,
	}
	api, err := GetAPIByIds(reqData.MenuIDs)
	if err != nil {
		return err
	}
	// 创建角色
	if err := global.DB.Transaction(func(tx *gorm.DB) error {
		if err := tx.Create(&role).Error; err != nil {
			return err
		}
		// 绑定角色菜单
		if err := tx.Model(&role).Association("Menus").Append(menus); err != nil {
			return err
		}
		if len(api) == 0 {
			return nil
		}
		// 绑定角色权限
		roleIdStr := strconv.Itoa(int(role.ID))
		forRole, err := utils.AddPermissionForRole(ApiToPolicies(roleIdStr, api))
		if err != nil {
			return err
		}
		if !forRole {
			return fmt.Errorf("添加权限失败")
		}
		return nil
	}); err != nil {
		return err
	}
	return nil
}

// UpdateRole 更新角色信息
func UpdateRole(reqData dto.UpdateRoleDTO, updateUser *Users) error {
	menus, err := GetMenusByIds(reqData.MenuIDs)
	if err != nil {
		return err
	}
	roleIdStr := strconv.Itoa(int(reqData.ID))
	//查询角色
	var role Roles
	if err := global.DB.First(&role, reqData.ID).Error; err != nil {
		return err
	}
	if err := global.DB.Transaction(func(tx *gorm.DB) error {
		//更新角色信息
		if err := tx.Model(&role).Updates(map[string]interface{}{
			"name":        reqData.Name,
			"describe":    reqData.Describe,
			"is_usable":   reqData.IsUsable,
			"modifier_id": updateUser.ID,
		}).Error; err != nil {
			return err
		}
		//更新角色菜单
		if err := tx.Model(&role).Association("Menus").Replace(menus); err != nil {
			return err
		}

		//更新角色权限
		api, err := GetAPIByIds(reqData.MenuIDs)
		if err != nil {
			return err
		}
		if len(api) == 0 { // 没有api 无需执行更新
			return nil
		}
		// 判断是否有策略在数据库，如果有在删除。
		if utils.GetPermissionForRole(roleIdStr) {
			role, err := utils.RemovePermissionForRole(roleIdStr)
			if err != nil {
				return err
			}
			if !role {
				return fmt.Errorf("删除权限失败")
			}
		}
		forRole, err := utils.AddPermissionForRole(ApiToPolicies(roleIdStr, api))
		if err != nil {
			return err
		}
		if !forRole {
			return fmt.Errorf("添加权限失败")
		}
		return nil
	}); err != nil {
		return err
	}

	return nil
}

// DeleteRole  删除角色并删除角色权限关联以及删除角色菜单关联
func DeleteRole(id string) error {
	var role Roles
	if err := global.DB.First(&role, id).Error; err != nil {
		return err
	}
	if err := global.DB.Transaction(func(tx *gorm.DB) error {
		// 删除角色与菜单关联
		if err := tx.Model(&role).Association("Menus").Clear(); err != nil {
			return err
		}
		// 删除角色与用户的关联
		if err := tx.Model(&role).Association("Users").Clear(); err != nil {
			return err
		}
		// 删除角色
		if err := tx.Unscoped().Delete(&role).Error; err != nil {
			return err
		}
		// 删除角色权限
		// 判断是否有策略在数据库，如果有在删除。
		if utils.GetPermissionForRole(id) {
			role, err := utils.RemovePermissionForRole(id)
			if err != nil {
				return err
			}
			if !role {
				return fmt.Errorf("删除权限失败")
			}
		}
		return nil
	}); err != nil {
		return err
	}
	return nil
}

// DeleteRoles 批量删除角色
func DeleteRoles(data dto.DelRoleByIDsReq) error {
	ids := data.Ids
	var roles []Roles
	if err := global.DB.Find(&roles, ids).Error; err != nil {
		return err
	}
	var rolesIdStr []string
	for _, v := range ids {
		rolesIdStr = append(rolesIdStr, strconv.Itoa(v))
	}
	if err := global.DB.Transaction(func(tx *gorm.DB) error {
		// 删除角色与菜单关联
		if err := tx.Model(&roles).Association("Menus").Clear(); err != nil {
			return err
		}
		// 删除角色与用户的关联
		if err := tx.Model(&roles).Association("Users").Clear(); err != nil {
			return err
		}
		if err := tx.Unscoped().Delete(&roles).Error; err != nil {
			return err
		}
		// 删除角色权限
		for _, v := range rolesIdStr {
			// 判断是否有策略在数据库，如果有在删除。
			if utils.GetPermissionForRole(v) {
				role, err := utils.RemovePermissionForRole(v)
				if err != nil {
					return err
				}
				if !role {
					return fmt.Errorf("删除权限失败")
				}
			}
		}
		return nil
	}); err != nil {
		return err
	}

	return nil
}

// ApiToPolicies api转policies-权限
func ApiToPolicies(roleId string, api []Menus) (policies [][]string) {
	for _, v := range api {
		policies = append(policies, []string{roleId, v.URL, v.RequestType})
	}
	return
}
